package info.marlan.sipgate;

import info.marlan.sipgate.model.HistoryEntry;
import info.marlan.sipgate.model.PhoneBookEntry;
import info.marlan.sipgate.model.PriceType;
import info.marlan.sipgate.model.SipgateAccount;
import info.marlan.util.ConvertUtil;
import info.marlan.util.DBHelper;
import info.marlan.util.TopExceptionHandler;
import info.marlan.util.VCardtools;

import java.net.URI;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.xmlrpc.android.XMLRPCClient;
import org.xmlrpc.android.XMLRPCException;
import org.xmlrpc.android.XMLRPCMethod;

import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
/**
 * 
 * @author mark@marlan.info
 *
 */

public class SipGateService extends Service implements Runnable {
	
	public static String STRING_ALL = "all";
	public static String KEY_PREFERENCES_LASTUPDATE = "info.marlan.sipgate.lastphonebookupdate";
	public static String KEY_PREFERENCES_BACKGROUND_DATA_NOTIFICATION = "info.marlan.sipgate.bgnotification";
	public static int[] updateIntervall = new int[]{20,30,60,120};
	private static Object lock = new Object();
	private static boolean threadRunning = false;
	private static boolean pendingUpdate = false;
	private static Map<String,XMLRPCClient> clients = new HashMap<String,XMLRPCClient>();
	public static String ENDPOINT_BASIC = "https://samurai.sipgate.net/RPC2";
	public static String ENDPOINT_TEAM = "https://api.sipgate.net/RPC2";

	private DBHelper dbHelper;
	private SharedPreferences preferences;
	private static String[] status;
	private static String[] tos;
	
	private boolean newEntryExists = false;
	
	@Override
	public IBinder onBind(Intent intent) {
		return null;
		
	}
	
	
	
	public void run() {
		
		//Log.v(WidgetProvider.LOGTAG, "SipgateService#run()");
		newEntryExists = false;
		List<SipgateAccount> accounts = dbHelper.getAccounts();
		try{
			if(accounts == null){
				//Log.v(WidgetProvider.LOGTAG, "accounts == null");
				if(dbHelper!=null){
				dbHelper.cleanup();
				dbHelper=null;
				}
				stopSelf();
				return;
			}

			
			checkBackgroundDataEnabled();
		
			for(SipgateAccount account:accounts){
					updateHistory(account);
					if(!isAlreadyRunToday()){
						updatePhoneBook(account);
					}
					updateBalance(account);
					updateAppWidget(account);
			}
		
		}finally{
			if(accounts != null){
				schedule();
			}
			pendingUpdate = false;
			threadRunning = false;
		}
	}

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		 Thread.setDefaultUncaughtExceptionHandler(new TopExceptionHandler(this));
		checkForCrashReport();
		if(dbHelper==null){
			dbHelper  = new DBHelper(this);
		}
		if(preferences==null){
			preferences = PreferenceManager
			.getDefaultSharedPreferences(this);
		}
		if(tos==null){
			tos = getResources().getStringArray(R.array.tos);
		}
		if(status==null){
			status = getResources().getStringArray(R.array.status);
		}
	//	boolean callFromOnUpdate = intent.getBooleanExtra(SipGateService.ON_UPDATE, false);
	//	Log.v(WidgetProvider.LOGTAG, "SipgateService#onStart()");
        synchronized (lock) {     	  
        	
        	if (!threadRunning) {
        		//Log.v(WidgetProvider.LOGTAG, "SipgateService#startthread()");
        	 	threadRunning = true;
                new Thread(this).start();
        	}else{
        		//Log.v(WidgetProvider.LOGTAG, "SipgateService#pendingupdate");
        		pendingUpdate = true;
        	}
        }
	}
	
	public void checkBackgroundDataEnabled(){
		if(!XMLRPCMethod.isBackgroundDataEnabled(this)){
			//Log.v(WidgetProvider.LOGTAG, "isBackgroundDataEnabled:no");
			if(!preferences.getBoolean(SipGateService.KEY_PREFERENCES_BACKGROUND_DATA_NOTIFICATION, false)){
				notifyUserBackGroundData();
			}
			Editor preferencesEditor = preferences.edit();
	        preferencesEditor.putBoolean(SipGateService.KEY_PREFERENCES_BACKGROUND_DATA_NOTIFICATION, true);
	    	preferencesEditor.commit();  
		}else{
		//	Log.v(WidgetProvider.LOGTAG, "isBackgroundDataEnabled:yes");
			Editor preferencesEditor = preferences.edit();
	        preferencesEditor.putBoolean(SipGateService.KEY_PREFERENCES_BACKGROUND_DATA_NOTIFICATION, false);
	    	preferencesEditor.commit();  
		}
	}


	public XMLRPCClient getClient(int appWidgetId){
			if(dbHelper==null){
				dbHelper  = new DBHelper(this);
			}
	 	  SipgateAccount account = dbHelper.getAccount(appWidgetId);
    	  XMLRPCClient client = clients.get(account.getId());
    	  if(client == null){
    		  client = new XMLRPCClient(URI.create(account.getEndpoint()), account.getUserName(), account.getPassword());
    		  clients.put(account.getId(), client);
    	  }
    	  return client;
	}
	
	public boolean isAlreadyRunToday(){
		final int dayOfYear = Calendar.getInstance().get(Calendar.DAY_OF_YEAR);
		final int lastUpdateDayOfYear = preferences.getInt(SipGateService.KEY_PREFERENCES_LASTUPDATE, 0);
		//Log.v(WidgetProvider.LOGTAG, "dayOfYear: " + dayOfYear + "lastUpdateDayOfYear :" +lastUpdateDayOfYear);
		if(dayOfYear==lastUpdateDayOfYear ){
			return true;
		}
		
		Editor preferencesEditor = preferences.edit();
        preferencesEditor.putInt(SipGateService.KEY_PREFERENCES_LASTUPDATE, dayOfYear);
    	preferencesEditor.commit();  
		
		return false;
	}
    
    
	@Override
	public void onDestroy() {
		super.onDestroy();
		if(dbHelper!=null){
		dbHelper.cleanup();
		dbHelper=null;
		}
	}

	@Override
	public void onCreate() {
		super.onCreate();
	}
	
	public Object[] getHistoryParam(HistoryEntry latest, SipgateAccount account){
		Object[] params = null;
        if(latest!=null ){
        	Map periodStart = new HashMap();
          	periodStart.put("PeriodStart",latest.getTimestamp());
          	params = new Object[]{periodStart};	
        }else{
        	Calendar c = Calendar.getInstance();
        	c.add(Calendar.MONTH, -1);
        	Map periodStart = new HashMap();
          	periodStart.put("PeriodStart",c.getTime());
          	params = new Object[]{periodStart};
        }
      	        
        if(account.getSipgateUri()!=null && !STRING_ALL.equals(account.getSipgateUri())){
        	Map localUriList = new HashMap();
        	List<String> uris = new ArrayList<String>();
        	uris.add(account.getSipgateUri());
        	localUriList.put("LocalUriList",uris);
        	if(params==null){
            	params =new Object[]{ localUriList} ;
        	}else{
        		params = new Object[]{localUriList,params[0]};
        	}
        }
        
        return params;
	}
	
	public void updateHistory(SipgateAccount account){
		if(!XMLRPCMethod.isConnected(this)){
			return;
		}
        HistoryEntry latest = dbHelper.getLatestHistoryEntry(account.getWidgetId());
        XMLRPCClient client = getClient(account.getWidgetId());
        try {
			final Object result = client.callEx("samurai.HistoryGetByDate",  getHistoryParam(latest, account));
			List<HistoryEntry> entries = ConvertUtil.getHistoryEntries(result,account.getWidgetId());
			if(entries==null||entries.size()==0){
				return;
			}
			for(HistoryEntry entry:entries){
				if(latest!=null && latest.getTimestamp().getTime() < entry.getTimestamp().getTime() ){
					newEntryExists = true;
				}
				dbHelper.updateHistory(entry);
			}  
		} catch (XMLRPCException e) {
			Log.e(WidgetProvider.LOGTAG,e.getMessage());
		}
	}
	
	public void updateBalance(SipgateAccount account){
		if(!XMLRPCMethod.isConnected(this)){
			return;
		}
        XMLRPCClient client = getClient(account.getWidgetId());
        try {
			final Object result = client.callEx("samurai.BalanceGet", null);
			PriceType balance = ConvertUtil.getBalance(result, account.getWidgetId());
			account.setBalance(balance);
		} catch (XMLRPCException e) {
			Log.e(WidgetProvider.LOGTAG,e.getMessage());
		}
		
	}
	
	
	public void updatePhoneBook(SipgateAccount account){
		if(!XMLRPCMethod.isConnected(this)){
			return;
		}
		XMLRPCClient client = getClient(account.getWidgetId());
		try {
			final Object result = client.callEx("samurai.PhonebookListGet",null);
			List<String> entryList = new ArrayList<String>();
			List<PhoneBookEntry> entries = ConvertUtil.getPhoneBookEntries(result,account.getWidgetId(),entryList);
			if(entries==null||entries.size()==0){
				return;
			}
			
		    Object[] params = new Object[1];
      	  	Map m = new HashMap();
      	  	m.put("EntryIDList",entryList);
      	  	params[0] = m ;
		        
      	  	final Object resultEntries = client.callEx("samurai.PhonebookEntryGet",params);
			
      	  	List<PhoneBookEntry> phoneBookentries = ConvertUtil.updatePhoneBookEntries(resultEntries,account.getWidgetId(),entries);
      	  	if(phoneBookentries==null){
      	  		return;
      	  	}
      	  	for(PhoneBookEntry entry:phoneBookentries){
      	  	if(entry.getEntry()!=null){
      	  		dbHelper.addPhoneBookEntry(entry);
      	  	}
      	  		
      	  	}
		} catch (XMLRPCException e) {
			Log.e(WidgetProvider.LOGTAG,e.getMessage());
		}
	}
	
    public void updateAppWidget(
            SipgateAccount account) {
          
    	HistoryEntry entry = dbHelper.getLatestHistoryEntry( account.getWidgetId());
    	if(entry!=null){
    		PhoneBookEntry phoneBook = 
    			dbHelper.findPhoneBookEntry(ConvertUtil.getBaseNumber(entry.getRemoteUri()), account.getWidgetId());
    		if(phoneBook!=null){
    			entry.setPhoneBookName(VCardtools.readVCardName(phoneBook.getEntry()));
    		}
    		AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
    		WidgetProvider.updateAppWidgetView(this, appWidgetManager, entry, account);
    		if(newEntryExists && isNotifyEvent(entry, account)){
    			notifyUser(entry);
    		}
    	}
    	
    }
    
    public boolean isNotifyEvent(HistoryEntry entry, SipgateAccount account){
    	if(!account.isNotify()){
    		return false;
    	}
    	String sStatus = status[account.getNotifyStatus()];
    	String sTos = tos[account.getNotifyTos()];
    	boolean isStatus = false;
    	boolean isTos = false;
    	if(STRING_ALL.equals(sStatus) || sStatus.equalsIgnoreCase(entry.getStatus()) ){
    		isStatus = true;
    	}
    	if(STRING_ALL.equals(sTos) || sTos.equalsIgnoreCase(entry.gettOS()) ){
    		isTos = true;
    	}
    	//Log.v(WidgetProvider.LOGTAG, "sStatus is: " + sStatus + " entry is: " +entry.getStatus() + " " + isStatus );
    	//Log.v(WidgetProvider.LOGTAG, "sTos is: " + sTos + " entry is: " +entry.gettOS() + " " + isTos );
    	return isStatus && isTos;
    }
    
    
    
    public void schedule(){

    	if(pendingUpdate){
    		Calendar c = Calendar.getInstance();
    		c.add(Calendar.SECOND, 30);
    		schedule(c);
    		stopSelf();
    		return;
    	}
    	
		int interval = preferences.getInt(AppWidgetConfigure.KEY_PREFERENCES_INTERVAL,1);
		int from = preferences.getInt(AppWidgetConfigure.KEY_PREFERENCES_FROM,7) + 1;
		int to = preferences.getInt(AppWidgetConfigure.KEY_PREFERENCES_TO,21) + 1;
		Calendar c = Calendar.getInstance();
		c.add(Calendar.MINUTE, updateIntervall[interval]);
		Calendar f = Calendar.getInstance();
		f.set(Calendar.HOUR_OF_DAY, from);
		f.set(Calendar.MINUTE, 0);
		f.set(Calendar.SECOND, 0);
		Calendar t = Calendar.getInstance();
		t.set(Calendar.HOUR_OF_DAY, to);
		t.set(Calendar.MINUTE, 0);
		t.set(Calendar.SECOND, 0);
		boolean daySwitched = false;
		while( c.getTimeInMillis() > t.getTimeInMillis() || c.getTimeInMillis() < f.getTimeInMillis()){
			if(c.get(Calendar.DAY_OF_YEAR) != t.get(Calendar.DAY_OF_YEAR)  ){
				if(daySwitched){
					break;
				}
				f.add(Calendar.DAY_OF_YEAR, 1);
				t.add(Calendar.DAY_OF_YEAR, 1);
				daySwitched=true;
			}
			
			c.add(Calendar.MINUTE, 20);
		}
		
		
		schedule(c);
        stopSelf();
    }
    
    public void schedule(Calendar c){
    	//Log.v(WidgetProvider.LOGTAG, "Schedule next update at: " +c.getTime());  
    	long nextUpdate = c.getTimeInMillis();
        Intent updateIntent = new Intent();
        updateIntent.setClass(this,SipGateService.class);
        PendingIntent pendingIntent = PendingIntent.getService(this, 0, updateIntent, 0);
        AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
    }
    
    public void notifyUserBackGroundData(){
    //	Log.v(WidgetProvider.LOGTAG,"send notification");
    //	Intent contentIntent = new Intent(Intent.,Uri.parse(ConvertUtil.getCallNumber(entry.getRemoteUri())));
    	notifyUser(getString(R.string.app_name), getString(R.string.background_data_disabled),null);
    }
    
    public void notifyUser(HistoryEntry entry){
    	StringBuilder message = new StringBuilder();
    	message.append(entry.getStatus()).append(" ");
    	message.append(entry.gettOS()).append(" ").append(entry.getPhoneBookName());
    	Intent contentIntent = new Intent(Intent.ACTION_DIAL,Uri.parse(ConvertUtil.getCallNumber(entry.getRemoteUri())));
    	notifyUser(message.toString(),message.toString(), contentIntent);
    }
    
    public void notifyUser(String title, String message, Intent intent){
    	NotificationManager notificationManager = 
    		(NotificationManager)getSystemService(
    		Context.NOTIFICATION_SERVICE);
    	
    	Notification notification = new Notification( 
    			android.R.drawable.stat_notify_error,message,
    			System.currentTimeMillis());
    	long[] vibration = new long[]{100,500,250,1000};
    	notification.vibrate = vibration;
    	
    	PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);
    	notification.setLatestEventInfo(getApplicationContext(),title,message, pendingIntent);
    	//Log.v(WidgetProvider.LOGTAG, "NotifyUser");
    	notificationManager.notify(123456, notification);
    }
    
    public void checkForCrashReport(){
    	if(!CrashReportActivity.fileExists(CrashReportActivity.CRASHREPORT, this)){
    		return;
    	}
    	Intent intent = new Intent (getApplication(),CrashReportActivity.class); 
    	//intentToSaveRecord.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
    	//startActivity(intentToSaveRecord); 
    	notifyUser(getString(R.string.crnotifytitle),getString(R.string.crnotifymessage), intent);
    }



}
